#include "Multiplier.h"
#include "types.h"
#include "verilated.h"
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <utility>

Multiplier::Multiplier() {
    ctx = new VerilatedContext;

    top = new Top{ctx};

#ifdef CONFIG_VTRACE
    ctx->traceEverOn(true);
    tfp = new VerilatedVcdC;
    top->trace(tfp, 99);
    tfp->open("vtrace.vcd");
#endif
}

Multiplier::~Multiplier() {
#ifdef CONFIG_VTRACE
    tfp->dump(ctx->time());
    tfp->close();
    delete tfp;
#endif
    top->final();
    delete top;
    delete ctx;
}

void Multiplier::step() {
    top->eval();
#ifdef CONFIG_VTRACE
    tfp->dump(ctx->time());
    ctx->timeInc(1);
#endif
}

void Multiplier::single_cycle() {
    top->clock = 0; step();
    top->clock = 1; step();
}

void Multiplier::reset() {
    top->reset = 1;
    for (int i = 0; i < 5; ++i) {
        single_cycle();
    }
    top->reset = 0;
}

std::pair<UIntD, UIntD> Multiplier::UU(UInt x, UInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->signs = 0;
    while (!top->done) {
        single_cycle();
    }
    auto answer = UIntD(x) * UIntD(y);
#ifdef CONFIG_ISA64
    __uint128_t temp0 = top->result.at(0);
    __uint128_t temp1 = top->result.at(1);
    __uint128_t temp2 = top->result.at(2);
    __uint128_t temp3 = top->result.at(3);
    __uint128_t result = temp0 | (temp1 << 32) | (temp2 << 64) | (temp3 << 96);
#else
    UIntD result = top->result;
#endif
    return std::make_pair(UIntD(result), UIntD(answer));
}

std::pair<UIntD, UIntD> Multiplier::US(UInt x, SInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->signs = 0b01;
    while (!top->done) {
        single_cycle();
    }
    auto answer = UIntD(x) * SIntD(y);
#ifdef CONFIG_ISA64
    __uint128_t temp0 = top->result.at(0);
    __uint128_t temp1 = top->result.at(1);
    __uint128_t temp2 = top->result.at(2);
    __uint128_t temp3 = top->result.at(3);
    __uint128_t result = temp0 | (temp1 << 32) | (temp2 << 64) | (temp3 << 96);
#else
    UIntD result = top->result;
#endif
    return std::make_pair(UIntD(result), UIntD(answer));
}
std::pair<UIntD, UIntD> Multiplier::SU(SInt x, UInt y)  {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->signs = 0b10;
    while (!top->done) {
        single_cycle();
    }
    auto answer = SIntD(x) * UIntD(y);
#ifdef CONFIG_ISA64
    __uint128_t temp0 = top->result.at(0);
    __uint128_t temp1 = top->result.at(1);
    __uint128_t temp2 = top->result.at(2);
    __uint128_t temp3 = top->result.at(3);
    __uint128_t result = temp0 | (temp1 << 32) | (temp2 << 64) | (temp3 << 96);
#else
    UIntD result = top->result;
#endif
    return std::make_pair(UIntD(result), UIntD(answer));
}
std::pair<UIntD, UIntD>  Multiplier::SS(SInt x, SInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->signs = 0b11;
    while (!top->done) {
        single_cycle();
    }
    auto answer = SIntD(x) * SIntD(y);
#ifdef CONFIG_ISA64
    __uint128_t temp0 = top->result.at(0);
    __uint128_t temp1 = top->result.at(1);
    __uint128_t temp2 = top->result.at(2);
    __uint128_t temp3 = top->result.at(3);
    __uint128_t result = temp0 | (temp1 << 32) | (temp2 << 64) | (temp3 << 96);
#else
    UIntD result = top->result;
#endif
    return std::make_pair(UIntD(result), UIntD(answer));
}
